Оптимизация производственных расходов металлургического комбината «Стальная птица»¶
Table of Contents
- 1 Загрузка данных
- 2 Исследовательский анализ и предобработка данных
- 2.1 Функции для анализа и предобработки данных
- 2.2 Анализ и предобработка данных датасета
data_arc_new - 2.3 Анализ и предобработка данных датасета
data_bulk_new - 2.4 Анализ и предобработка данных датасета
data_bulk_time_new - 2.5 Анализ и предобработка данных датасета
data_gas_new - 2.6 Анализ и предобработка данных датасета
data_temp_new - 2.7 Анализ и предобработка данных датасета
data_wire_new - 2.8 Анализ и предобработка данных датасета
data_wire_time_new
- 3 Объединение данных
- 4 Исследовательский анализ и предобработка данных объединённого датафрейма
- 5 Подготовка данных
- 6 Обучение моделей машинного обучения
- 7 Выбор лучшей модели
- 8 Общий вывод
ЦЕЛЬ ПРОЕКТА: Построить модель, которая будет предсказывать температуру сплава.
Описание проекта: промышленность
Чтобы оптимизировать производственные расходы, металлургический комбинат «Стальная птица» решил уменьшить потребление электроэнергии на этапе обработки стали. Для этого комбинату нужно контролировать температуру сплава.
Ваша задача — построить модель, которая будет её предсказывать.
Описание данных
- Файл
data_arc_new.csv- данные об электродах:
key— номер партии;Начало нагрева дугой— время начала нагрева;Конец нагрева дугой— время окончания нагрева;Активная мощность— значение активной мощности;Реактивная мощность— значение реактивной мощности.
- Файл
data_bulk_new.csv- данные о подаче сыпучих материалов (объём):
key— номер партии;Bulk 1…Bulk 15— объём подаваемого материала.
- Файл
data_bulk_time_new.csv- данные о подаче сыпучих материалов (время):
key— номер партии;Bulk 1…Bulk 15— время подачи материала.
- Файл
data_gas_new.csv- данные о продувке сплава газом;
key— номер партии;Газ 1— объём подаваемого газа.
- Файл
data_temp_new.csv- результаты измерения температуры:
key— номер партии;Время замера— время замера;Температура— значение температуры.
- Файл
data_wire_new.csv- данные о проволочных материалах (объём):
key— номер партии;Wire 1…Wire 9— объём подаваемых проволочных материалов.
- Файл
data_wire_time_new.csv- данные о проволочных материалах (время):
key— номер партии;Wire 1…Wire 9— время подачи проволочных материалов.
Во всех файлах столбец key содержит номер партии. В файлах может быть несколько строк с одинаковым значением key: они соответствуют разным итерациям обработки.
Загрузка библиотек
%%capture
%pip install -qq scikit-learn catboost seaborn shap phik
# Обновление scikit-learn (на случай, если уже установлен устаревший)
%pip install -q -U scikit-learn
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import re
import phik
import multiprocessing
from IPython.display import display
import os
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import mean_absolute_error
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import (
StandardScaler, MinMaxScaler
)
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.dummy import DummyRegressor
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from catboost import CatBoostRegressor
import warnings
warnings.filterwarnings('ignore')
# Настройки вывода графиков
plt.rcParams["axes.titlesize"] = 16 # Размер шрифта
plt.rcParams["axes.titleweight"] = "bold" # Толщина шрифта
# Константы
RANDOM_STATE = 160625
TEST_SIZE = 0.25
Загрузка данных¶
Функции для загрузки и отображенияи информации о датасете
from pathlib import Path
class CSVLoader:
"""Загрузчик CSV-файлов с двумя шаблонами путей."""
PATH_TEMPLATES = [
Path("C:/Data-science/ds_csv/{}"),
Path("/datasets/{}"),
]
def __init__(self):
self.datasets = {}
def _find_csv_path(self, filename):
"""Ищет CSV-файл по шаблонам путей."""
for template in self.PATH_TEMPLATES:
path = template.with_name(filename)
if path.exists():
print(f"Файл найден: {path}")
return path
raise FileNotFoundError(
f"Файл '{filename}' не найден по путям:\n" +
"\n".join(str(template.with_name(filename)) for template in self.PATH_TEMPLATES)
)
def load_csv(self, name, filename, **kwargs):
"""Загружает один CSV-файл."""
try:
path = self._find_csv_path(filename)
df = pd.read_csv(path, **kwargs)
self.datasets[name] = df
print(f"✅ '{name}' загружен ({filename})")
return df
except Exception as e:
print(f"❌ Ошибка при загрузке '{name}': {e}")
return None
def load_many(self, files_config):
"""Загружает несколько CSV-файлов по конфигурации."""
for name, filename in files_config.items():
self.load_csv(name, filename)
return self.datasets
# Определяем TERM_SIZE
try:
TERM_SIZE = os.get_terminal_size()
except:
TERM_SIZE = type('', (), {'columns': 80})()
def show_info(df):
display(df.head(5))
print()
df.info()
print('=' * TERM_SIZE.columns)
print(f'\033[1mКоличество дубликатов: \033[0m{df.duplicated().sum()}')
print('=' * TERM_SIZE.columns)
print("\033[1mПроцент пропусков от всего датасета:\033[0m")
print()
formatted_output = df.isna().mean().sort_values(ascending=False).apply(lambda x: "{:.1%}".format(x))
print(formatted_output) #
print('=' * TERM_SIZE.columns)
print(f'\033[1mОписание:\033[0m ')
display(df.describe())
print('=' * TERM_SIZE.columns)
print(f'\033[1mРазмер: \033[0m{df.shape}')
Загрузка датасетов¶
loader = CSVLoader()
# Словарь: ключ — имя датасета, значение — имя CSV-файла
files = {
'data_arc_new': 'data_arc_new.csv',
'data_bulk_new': 'data_bulk_new.csv',
'data_bulk_time_new': 'data_bulk_time_new.csv',
'data_gas_new': 'data_gas_new.csv',
'data_temp_new': 'data_temp_new.csv',
'data_wire_new': 'data_wire_new.csv',
'data_wire_time_new': 'data_wire_time_new.csv'
}
all_data = loader.load_many(files)
print("Загружено:", list(all_data.keys()))
Файл найден: C:\Data-science\ds_csv\data_arc_new.csv ✅ 'data_arc_new' загружен (data_arc_new.csv) Файл найден: C:\Data-science\ds_csv\data_bulk_new.csv ✅ 'data_bulk_new' загружен (data_bulk_new.csv) Файл найден: C:\Data-science\ds_csv\data_bulk_time_new.csv ✅ 'data_bulk_time_new' загружен (data_bulk_time_new.csv) Файл найден: C:\Data-science\ds_csv\data_gas_new.csv ✅ 'data_gas_new' загружен (data_gas_new.csv) Файл найден: C:\Data-science\ds_csv\data_temp_new.csv ✅ 'data_temp_new' загружен (data_temp_new.csv) Файл найден: C:\Data-science\ds_csv\data_wire_new.csv ✅ 'data_wire_new' загружен (data_wire_new.csv) Файл найден: C:\Data-science\ds_csv\data_wire_time_new.csv ✅ 'data_wire_time_new' загружен (data_wire_time_new.csv) Загружено: ['data_arc_new', 'data_bulk_new', 'data_bulk_time_new', 'data_gas_new', 'data_temp_new', 'data_wire_new', 'data_wire_time_new']
# Создание переменных с именами как в словаре
for name, df in all_data.items():
globals()[name] = df
Анализ загруженных датасетов¶
Датасет data_arc_new¶
show_info(data_arc_new)
| key | Начало нагрева дугой | Конец нагрева дугой | Активная мощность | Реактивная мощность | |
|---|---|---|---|---|---|
| 0 | 1 | 2019-05-03 11:02:14 | 2019-05-03 11:06:02 | 0.305130 | 0.211253 |
| 1 | 1 | 2019-05-03 11:07:28 | 2019-05-03 11:10:33 | 0.765658 | 0.477438 |
| 2 | 1 | 2019-05-03 11:11:44 | 2019-05-03 11:14:36 | 0.580313 | 0.430460 |
| 3 | 1 | 2019-05-03 11:18:14 | 2019-05-03 11:24:19 | 0.518496 | 0.379979 |
| 4 | 1 | 2019-05-03 11:26:09 | 2019-05-03 11:28:37 | 0.867133 | 0.643691 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14876 entries, 0 to 14875 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 14876 non-null int64 1 Начало нагрева дугой 14876 non-null object 2 Конец нагрева дугой 14876 non-null object 3 Активная мощность 14876 non-null float64 4 Реактивная мощность 14876 non-null float64 dtypes: float64(2), int64(1), object(2) memory usage: 581.2+ KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: key 0.0% Начало нагрева дугой 0.0% Конец нагрева дугой 0.0% Активная мощность 0.0% Реактивная мощность 0.0% dtype: object ======================================================================================================================== Описание:
| key | Активная мощность | Реактивная мощность | |
|---|---|---|---|
| count | 14876.000000 | 14876.000000 | 14876.000000 |
| mean | 1615.220422 | 0.662752 | 0.438986 |
| std | 934.571502 | 0.258885 | 5.873485 |
| min | 1.000000 | 0.223120 | -715.479924 |
| 25% | 806.000000 | 0.467115 | 0.337175 |
| 50% | 1617.000000 | 0.599587 | 0.441639 |
| 75% | 2429.000000 | 0.830070 | 0.608201 |
| max | 3241.000000 | 1.463773 | 1.270284 |
========================================================================================================================
Размер: (14876, 5)
Вывод по загрузке датасета data_arc_new
- В датасете 14876 строк и 5 столбцов;
- Параметр
keyповторяется, что говорит о многократном нагреве одной партии; - Названия столбцов не соответствуют PEP8;
- Данные времени о начале и окончании нагрева дугой имеют формат отличный от datetime;
- В датасете отсутствуют пропуски;
- В датасете отсутствуют дубликаты;
- Минимальное значение реактивной мощности меньше 0 - явная аномалия требующая удаления.
Датасет data_bulk_new¶
show_info(data_bulk_new)
| key | Bulk 1 | Bulk 2 | Bulk 3 | Bulk 4 | Bulk 5 | Bulk 6 | Bulk 7 | Bulk 8 | Bulk 9 | Bulk 10 | Bulk 11 | Bulk 12 | Bulk 13 | Bulk 14 | Bulk 15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | NaN | NaN | NaN | 43.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 206.0 | NaN | 150.0 | 154.0 |
| 1 | 2 | NaN | NaN | NaN | 73.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 206.0 | NaN | 149.0 | 154.0 |
| 2 | 3 | NaN | NaN | NaN | 34.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 205.0 | NaN | 152.0 | 153.0 |
| 3 | 4 | NaN | NaN | NaN | 81.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 207.0 | NaN | 153.0 | 154.0 |
| 4 | 5 | NaN | NaN | NaN | 78.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 203.0 | NaN | 151.0 | 152.0 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3129 entries, 0 to 3128 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3129 non-null int64 1 Bulk 1 252 non-null float64 2 Bulk 2 22 non-null float64 3 Bulk 3 1298 non-null float64 4 Bulk 4 1014 non-null float64 5 Bulk 5 77 non-null float64 6 Bulk 6 576 non-null float64 7 Bulk 7 25 non-null float64 8 Bulk 8 1 non-null float64 9 Bulk 9 19 non-null float64 10 Bulk 10 176 non-null float64 11 Bulk 11 177 non-null float64 12 Bulk 12 2450 non-null float64 13 Bulk 13 18 non-null float64 14 Bulk 14 2806 non-null float64 15 Bulk 15 2248 non-null float64 dtypes: float64(15), int64(1) memory usage: 391.2 KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: Bulk 8 100.0% Bulk 13 99.4% Bulk 9 99.4% Bulk 2 99.3% Bulk 7 99.2% Bulk 5 97.5% Bulk 10 94.4% Bulk 11 94.3% Bulk 1 91.9% Bulk 6 81.6% Bulk 4 67.6% Bulk 3 58.5% Bulk 15 28.2% Bulk 12 21.7% Bulk 14 10.3% key 0.0% dtype: object ======================================================================================================================== Описание:
| key | Bulk 1 | Bulk 2 | Bulk 3 | Bulk 4 | Bulk 5 | Bulk 6 | Bulk 7 | Bulk 8 | Bulk 9 | Bulk 10 | Bulk 11 | Bulk 12 | Bulk 13 | Bulk 14 | Bulk 15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 3129.000000 | 252.000000 | 22.000000 | 1298.000000 | 1014.000000 | 77.000000 | 576.000000 | 25.000000 | 1.0 | 19.000000 | 176.000000 | 177.000000 | 2450.000000 | 18.000000 | 2806.000000 | 2248.000000 |
| mean | 1624.383509 | 39.242063 | 253.045455 | 113.879045 | 104.394477 | 107.025974 | 118.925347 | 305.600000 | 49.0 | 76.315789 | 83.284091 | 76.819209 | 260.471020 | 181.111111 | 170.284747 | 160.513345 |
| std | 933.337642 | 18.277654 | 21.180578 | 75.483494 | 48.184126 | 81.790646 | 72.057776 | 191.022904 | NaN | 21.720581 | 26.060347 | 59.655365 | 120.649269 | 46.088009 | 65.868652 | 51.765319 |
| min | 1.000000 | 10.000000 | 228.000000 | 6.000000 | 12.000000 | 11.000000 | 17.000000 | 47.000000 | 49.0 | 63.000000 | 24.000000 | 8.000000 | 53.000000 | 151.000000 | 16.000000 | 1.000000 |
| 25% | 816.000000 | 27.000000 | 242.000000 | 58.000000 | 72.000000 | 70.000000 | 69.750000 | 155.000000 | 49.0 | 66.000000 | 64.000000 | 25.000000 | 204.000000 | 153.250000 | 119.000000 | 105.000000 |
| 50% | 1622.000000 | 31.000000 | 251.500000 | 97.500000 | 102.000000 | 86.000000 | 100.000000 | 298.000000 | 49.0 | 68.000000 | 86.500000 | 64.000000 | 208.000000 | 155.500000 | 151.000000 | 160.000000 |
| 75% | 2431.000000 | 46.000000 | 257.750000 | 152.000000 | 133.000000 | 132.000000 | 157.000000 | 406.000000 | 49.0 | 70.500000 | 102.000000 | 106.000000 | 316.000000 | 203.500000 | 205.750000 | 205.000000 |
| max | 3241.000000 | 185.000000 | 325.000000 | 454.000000 | 281.000000 | 603.000000 | 503.000000 | 772.000000 | 49.0 | 147.000000 | 159.000000 | 313.000000 | 1849.000000 | 305.000000 | 636.000000 | 405.000000 |
========================================================================================================================
Размер: (3129, 16)
Вывод по загрузке датасета data_bulk_new
- В датасете 3129 строк и 16 столбцов;
- Названия столбцов не соответствуют PEP8;
- В датасете отсутствуют дубликаты;
- В датасете большое количество пропусков.
Датасет data_bulk_time_new¶
show_info(data_bulk_time_new)
| key | Bulk 1 | Bulk 2 | Bulk 3 | Bulk 4 | Bulk 5 | Bulk 6 | Bulk 7 | Bulk 8 | Bulk 9 | Bulk 10 | Bulk 11 | Bulk 12 | Bulk 13 | Bulk 14 | Bulk 15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | NaN | NaN | NaN | 2019-05-03 11:28:48 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 11:24:31 | NaN | 2019-05-03 11:14:50 | 2019-05-03 11:10:43 |
| 1 | 2 | NaN | NaN | NaN | 2019-05-03 11:36:50 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 11:53:30 | NaN | 2019-05-03 11:48:37 | 2019-05-03 11:44:39 |
| 2 | 3 | NaN | NaN | NaN | 2019-05-03 12:32:39 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 12:27:13 | NaN | 2019-05-03 12:21:01 | 2019-05-03 12:16:16 |
| 3 | 4 | NaN | NaN | NaN | 2019-05-03 12:43:22 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 12:58:00 | NaN | 2019-05-03 12:51:11 | 2019-05-03 12:46:36 |
| 4 | 5 | NaN | NaN | NaN | 2019-05-03 13:30:47 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 13:30:47 | NaN | 2019-05-03 13:34:12 | 2019-05-03 13:30:47 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3129 entries, 0 to 3128 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3129 non-null int64 1 Bulk 1 252 non-null object 2 Bulk 2 22 non-null object 3 Bulk 3 1298 non-null object 4 Bulk 4 1014 non-null object 5 Bulk 5 77 non-null object 6 Bulk 6 576 non-null object 7 Bulk 7 25 non-null object 8 Bulk 8 1 non-null object 9 Bulk 9 19 non-null object 10 Bulk 10 176 non-null object 11 Bulk 11 177 non-null object 12 Bulk 12 2450 non-null object 13 Bulk 13 18 non-null object 14 Bulk 14 2806 non-null object 15 Bulk 15 2248 non-null object dtypes: int64(1), object(15) memory usage: 391.2+ KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: Bulk 8 100.0% Bulk 13 99.4% Bulk 9 99.4% Bulk 2 99.3% Bulk 7 99.2% Bulk 5 97.5% Bulk 10 94.4% Bulk 11 94.3% Bulk 1 91.9% Bulk 6 81.6% Bulk 4 67.6% Bulk 3 58.5% Bulk 15 28.2% Bulk 12 21.7% Bulk 14 10.3% key 0.0% dtype: object ======================================================================================================================== Описание:
| key | |
|---|---|
| count | 3129.000000 |
| mean | 1624.383509 |
| std | 933.337642 |
| min | 1.000000 |
| 25% | 816.000000 |
| 50% | 1622.000000 |
| 75% | 2431.000000 |
| max | 3241.000000 |
========================================================================================================================
Размер: (3129, 16)
Вывод по загрузке датасета data_bulk_time_new
- В датасете 3129 строк и 16 столбцов, аналогичное с датасетом с объемом подачи материалов
data_bulk_new; - Названия столбцов не соответствуют PEP8;
- Данные со временем имеют отличный от datetime формат;
- В датасете отсутствуют дубликаты;
- В датасете количество пропусков, аналогично с датасетом
data_bulk_new.
Датасет data_gas_new¶
show_info(data_gas_new)
| key | Газ 1 | |
|---|---|---|
| 0 | 1 | 29.749986 |
| 1 | 2 | 12.555561 |
| 2 | 3 | 28.554793 |
| 3 | 4 | 18.841219 |
| 4 | 5 | 5.413692 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3239 entries, 0 to 3238 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3239 non-null int64 1 Газ 1 3239 non-null float64 dtypes: float64(1), int64(1) memory usage: 50.7 KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: key 0.0% Газ 1 0.0% dtype: object ======================================================================================================================== Описание:
| key | Газ 1 | |
|---|---|---|
| count | 3239.000000 | 3239.000000 |
| mean | 1621.861377 | 11.002062 |
| std | 935.386334 | 6.220327 |
| min | 1.000000 | 0.008399 |
| 25% | 812.500000 | 7.043089 |
| 50% | 1622.000000 | 9.836267 |
| 75% | 2431.500000 | 13.769915 |
| max | 3241.000000 | 77.995040 |
========================================================================================================================
Размер: (3239, 2)
Вывод по загрузке датасета data_gas_new
- В датасете 3239 строк и 2 столбца;
- Названия столбцов не соответствуют PEP8;
- В датасете отсутствуют дубликаты;
- В датасете отсутствуют пропуски.
Датасет data_temp_new¶
show_info(data_temp_new)
| key | Время замера | Температура | |
|---|---|---|---|
| 0 | 1 | 2019-05-03 11:02:04 | 1571.0 |
| 1 | 1 | 2019-05-03 11:07:18 | 1604.0 |
| 2 | 1 | 2019-05-03 11:11:34 | 1618.0 |
| 3 | 1 | 2019-05-03 11:18:04 | 1601.0 |
| 4 | 1 | 2019-05-03 11:25:59 | 1606.0 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 18092 entries, 0 to 18091 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 18092 non-null int64 1 Время замера 18092 non-null object 2 Температура 14665 non-null float64 dtypes: float64(1), int64(1), object(1) memory usage: 424.2+ KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: Температура 18.9% key 0.0% Время замера 0.0% dtype: object ======================================================================================================================== Описание:
| key | Температура | |
|---|---|---|
| count | 18092.000000 | 14665.000000 |
| mean | 1616.460977 | 1590.722741 |
| std | 934.641385 | 20.394381 |
| min | 1.000000 | 1191.000000 |
| 25% | 807.750000 | 1580.000000 |
| 50% | 1618.000000 | 1590.000000 |
| 75% | 2429.000000 | 1599.000000 |
| max | 3241.000000 | 1705.000000 |
========================================================================================================================
Размер: (18092, 3)
Вывод по загрузке датасета data_temp_new
- В датасете 18092 строки и 3 столбца;
- Параметр
keyповторяется, что говорит о многократном нагреве одной партии; - Названия столбцов не соответствуют PEP8;
- В датасете отсутствуют дубликаты;
- В датасете присутствуют пропуски.
Датасет data_wire_new¶
show_info(data_wire_new)
| key | Wire 1 | Wire 2 | Wire 3 | Wire 4 | Wire 5 | Wire 6 | Wire 7 | Wire 8 | Wire 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 60.059998 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 2 | 96.052315 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | 3 | 91.160157 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3 | 4 | 89.063515 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 4 | 5 | 89.238236 | 9.11456 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3081 entries, 0 to 3080 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3081 non-null int64 1 Wire 1 3055 non-null float64 2 Wire 2 1079 non-null float64 3 Wire 3 63 non-null float64 4 Wire 4 14 non-null float64 5 Wire 5 1 non-null float64 6 Wire 6 73 non-null float64 7 Wire 7 11 non-null float64 8 Wire 8 19 non-null float64 9 Wire 9 29 non-null float64 dtypes: float64(9), int64(1) memory usage: 240.8 KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: Wire 5 100.0% Wire 7 99.6% Wire 4 99.5% Wire 8 99.4% Wire 9 99.1% Wire 3 98.0% Wire 6 97.6% Wire 2 65.0% Wire 1 0.8% key 0.0% dtype: object ======================================================================================================================== Описание:
| key | Wire 1 | Wire 2 | Wire 3 | Wire 4 | Wire 5 | Wire 6 | Wire 7 | Wire 8 | Wire 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| count | 3081.000000 | 3055.000000 | 1079.000000 | 63.000000 | 14.000000 | 1.000 | 73.000000 | 11.000000 | 19.000000 | 29.000000 |
| mean | 1623.426485 | 100.895853 | 50.577323 | 189.482681 | 57.442841 | 15.132 | 48.016974 | 10.039007 | 53.625193 | 34.155752 |
| std | 932.996726 | 42.012518 | 39.320216 | 99.513444 | 28.824667 | NaN | 33.919845 | 8.610584 | 16.881728 | 19.931616 |
| min | 1.000000 | 1.918800 | 0.030160 | 0.144144 | 24.148801 | 15.132 | 0.034320 | 0.234208 | 45.076721 | 4.622800 |
| 25% | 823.000000 | 72.115684 | 20.193680 | 95.135044 | 40.807002 | 15.132 | 25.053600 | 6.762756 | 46.094879 | 22.058401 |
| 50% | 1619.000000 | 100.158234 | 40.142956 | 235.194977 | 45.234282 | 15.132 | 42.076324 | 9.017009 | 46.279999 | 30.066399 |
| 75% | 2434.000000 | 126.060483 | 70.227558 | 276.252014 | 76.124619 | 15.132 | 64.212723 | 11.886057 | 48.089603 | 43.862003 |
| max | 3241.000000 | 330.314424 | 282.780152 | 385.008668 | 113.231044 | 15.132 | 180.454575 | 32.847674 | 102.762401 | 90.053604 |
========================================================================================================================
Размер: (3081, 10)
Вывод по загрузке датасета data_wire_new
- В датасете 3081 строка и 10 столбцов;
- Названия столбцов не соответствуют PEP8;
- В датасете отсутствуют дубликаты;
- В датасете присутствуют пропуски.
Датасет data_wire_time_new¶
show_info(data_wire_time_new)
| key | Wire 1 | Wire 2 | Wire 3 | Wire 4 | Wire 5 | Wire 6 | Wire 7 | Wire 8 | Wire 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2019-05-03 11:06:19 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 2 | 2019-05-03 11:36:50 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | 3 | 2019-05-03 12:11:46 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3 | 4 | 2019-05-03 12:43:22 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 4 | 5 | 2019-05-03 13:20:44 | 2019-05-03 13:15:34 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3081 entries, 0 to 3080 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3081 non-null int64 1 Wire 1 3055 non-null object 2 Wire 2 1079 non-null object 3 Wire 3 63 non-null object 4 Wire 4 14 non-null object 5 Wire 5 1 non-null object 6 Wire 6 73 non-null object 7 Wire 7 11 non-null object 8 Wire 8 19 non-null object 9 Wire 9 29 non-null object dtypes: int64(1), object(9) memory usage: 240.8+ KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: Wire 5 100.0% Wire 7 99.6% Wire 4 99.5% Wire 8 99.4% Wire 9 99.1% Wire 3 98.0% Wire 6 97.6% Wire 2 65.0% Wire 1 0.8% key 0.0% dtype: object ======================================================================================================================== Описание:
| key | |
|---|---|
| count | 3081.000000 |
| mean | 1623.426485 |
| std | 932.996726 |
| min | 1.000000 |
| 25% | 823.000000 |
| 50% | 1619.000000 |
| 75% | 2434.000000 |
| max | 3241.000000 |
========================================================================================================================
Размер: (3081, 10)
Вывод по загрузке датасета data_wire_time_new
- В датасете 3081 строка и 10 столбцов;
- Названия столбцов не соответствуют PEP8;
- В датасете отсутствуют дубликаты;
- В датасете пропуски аналогичны датасету
data_wire_new.
Вывод
- В датасетах столбцы на разных языках и много пропусков, принято решение пока пропуски оставим как есть.
- Во всех столбцах датафреймов столбцы с датами не соответствуют формату datetime.
Пропуски в столбцах могут возникать по разным причинам: из-за ошибок при передаче данных, сбоев в работе датчиков или отсутствия необходимости в выполнении определённых операций (например, нагрева или добавления присадок).
Исследовательский анализ и предобработка данных¶
Функции для анализа и предобработки данных¶
def convert_to_pep8_column_names(df: pd.DataFrame) -> pd.DataFrame:
'''
Приводит названия столбцов датафрейма к snake_case (PEP 8).
(работает только для англоязычных названий столбцов)
'''
new_columns = []
for col in df.columns:
# Заменяем пробелы и спецсимволы на "_"
col = re.sub(r'[\s\-+.,!@#$%^&*()]', '_', str(col))
# Разделяем CamelCase (например, "UserId" → "user_id")
col = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', col)
# Приводим к нижнему регистру и убираем повторяющиеся "_"
col = col.lower().replace('__', '_').strip('_')
# Удаляем последнее нижнее подчеркивание "_"
col = col.rstrip('_')
new_columns.append(col)
# Возвращаем датафрейм с новыми названиями
return df.rename(columns=dict(zip(df.columns, new_columns)))
def drop_high_na_columns(df: pd.DataFrame, threshold: float = 0.85) -> pd.DataFrame:
"""
Удаляет столбцы из DataFrame, в которых доля пропущенных значений >= threshold.
"""
# Вычисляем долю пропущенных значений для каждого столбца
na_fraction = df.isna().mean()
# Выбираем индексы столбцов, где доля NA >= threshold
columns_to_drop = na_fraction[na_fraction >= threshold].index
# Возвращаем новый DataFrame с удаленными столбцами
return df.drop(columns=columns_to_drop)
def show_num_variable(df, column, target=None):
'''
Функция отображает гистограмму распределения
и диаграмму размаха для числового столбца датафрейма,
с возможной группировкой по переменной target.
'''
sns.set(style='whitegrid')
f, axes = plt.subplots(2, 1, figsize=(14, 10), gridspec_kw={'height_ratios': [3, 1]}) # 2 строки, 1 столбец
# Подготовка данных (удаление NaN)
cols = [column] + ([target] if target is not None else [])
plot_df = df[cols].dropna()
# Гистограмма
axes[0].set_title(f'Гистограмма распределения {column}', fontsize=16)
axes[0].set_ylabel('Доля наблюдений', fontsize=14)
axes[0].set_xlabel(column, fontsize=14)
sns.histplot(
data=plot_df,
x=column,
hue=target if target is not None else None,
bins=20,
kde=True,
stat='probability',
ax=axes[0]
)
# Добавляем линии среднего и медианы
median = plot_df[column].median()
mean = plot_df[column].mean()
axes[0].axvline(median, color='red', linestyle='--', linewidth=2, label=f'Медиана: {median:.2f}')
axes[0].axvline(mean, color='blue', linestyle='-', linewidth=2, label=f'Среднее: {mean:.2f}')
axes[0].legend(fontsize=12)
# Диаграмма размаха
axes[1].set_title(f'Диаграмма размаха для {column}', fontsize=16)
axes[1].set_ylabel(column, fontsize=14)
if target is not None:
sns.boxplot(data=plot_df, y=target, x=column, orient='h', ax=axes[1])
axes[1].set_ylabel(target, fontsize=14)
axes[1].set_xlabel(column, fontsize=14)
else:
sns.boxplot(data=plot_df, x=column, orient='h', ax=axes[1])
axes[1].set_ylabel('')
axes[1].set_xlabel(column, fontsize=14)
plt.tight_layout()
plt.show()
Анализ и предобработка данных датасета data_arc_new¶
data_arc_new.head(1)
| key | Начало нагрева дугой | Конец нагрева дугой | Активная мощность | Реактивная мощность | |
|---|---|---|---|---|---|
| 0 | 1 | 2019-05-03 11:02:14 | 2019-05-03 11:06:02 | 0.30513 | 0.211253 |
Приведем названия столбцов к PEP8 и время в формат datetime
data_arc_new.columns = ['key', 'start_heating','end_heating', 'active_power', 'reactive_power']
# Проверка
data_arc_new.columns
Index(['key', 'start_heating', 'end_heating', 'active_power',
'reactive_power'],
dtype='object')
Изменим тип данных для столбцов start_heating и end_heating
cols_to_convert = ['start_heating','end_heating']
for col in cols_to_convert:
data_arc_new[col] = pd.to_datetime(data_arc_new[col], errors='coerce')
# Проверка
data_arc_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14876 entries, 0 to 14875 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 14876 non-null int64 1 start_heating 14876 non-null datetime64[ns] 2 end_heating 14876 non-null datetime64[ns] 3 active_power 14876 non-null float64 4 reactive_power 14876 non-null float64 dtypes: datetime64[ns](2), float64(2), int64(1) memory usage: 581.2 KB
В ходе загрузки данных было замечено аномальное значение реактивной мощности меньшей 0, уберем все такие аномалии
data_arc_new = data_arc_new.query('reactive_power > 0')
Добавим параметр связывающий реактивную и активную мощность нагревательной цепи используя формулу векторной взаимосвязи:
data_arc_new['full_power'] = np.sqrt(data_arc_new['active_power']**2 + data_arc_new['reactive_power']**2)
Добавим параметр времени необходимого на нагрев в секундах
data_arc_new['heating_time'] = (data_arc_new['end_heating'] - data_arc_new['start_heating']).dt.seconds
# Проверка
data_arc_new.head(3)
| key | start_heating | end_heating | active_power | reactive_power | full_power | heating_time | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 2019-05-03 11:02:14 | 2019-05-03 11:06:02 | 0.305130 | 0.211253 | 0.371123 | 228 |
| 1 | 1 | 2019-05-03 11:07:28 | 2019-05-03 11:10:33 | 0.765658 | 0.477438 | 0.902319 | 185 |
| 2 | 1 | 2019-05-03 11:11:44 | 2019-05-03 11:14:36 | 0.580313 | 0.430460 | 0.722536 | 172 |
Сгруппируем данные по партиям, т.к. в задаче нам необходимо предсказывать финальную температуру после нагрева.
data_arc_new_gp = data_arc_new.groupby(by='key').sum()
display(data_arc_new_gp.head())
data_arc_new_gp.shape
| active_power | reactive_power | full_power | heating_time | |
|---|---|---|---|---|
| key | ||||
| 1 | 3.036730 | 2.142821 | 3.718736 | 1098 |
| 2 | 2.139408 | 1.453357 | 2.588349 | 811 |
| 3 | 4.063641 | 2.937457 | 5.019223 | 655 |
| 4 | 2.706489 | 2.056992 | 3.400038 | 741 |
| 5 | 2.252950 | 1.687991 | 2.816980 | 869 |
(3214, 4)
После группировки датасета по партиям и добавления новых параметров, получили датасет с 3214 строками и 4 столбцами
# Формирование списка столбцов с количественными признаками
num_variables_col = data_arc_new_gp.select_dtypes(include=['number']).columns.to_list()
num_variables_col
['active_power', 'reactive_power', 'full_power', 'heating_time']
# Вывод графиков для количественных признаков датафрейма data_arc_new
for col in num_variables_col:
show_num_variable(data_arc_new_gp, col)
print('=' * TERM_SIZE.columns)
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
data_arc_new_gp.describe().round(3)
| active_power | reactive_power | full_power | heating_time | |
|---|---|---|---|---|
| count | 3214.000 | 3214.000 | 3214.000 | 3214.000 |
| mean | 3.067 | 2.254 | 3.811 | 794.545 |
| std | 1.209 | 0.895 | 1.503 | 332.491 |
| min | 0.268 | 0.196 | 0.332 | 57.000 |
| 25% | 2.235 | 1.631 | 2.775 | 571.000 |
| 50% | 2.985 | 2.177 | 3.694 | 770.000 |
| 75% | 3.775 | 2.788 | 4.697 | 983.000 |
| max | 12.376 | 8.949 | 15.288 | 4189.000 |
Вывод
Общие выводы по графикам:
- Распределение не симметрично, а смещено вправо;
- У них выраженный пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения, но они редкие;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
Вывод по графикам для параметра active_power:
-- Среднее значение активной мощности необходимой для нагрева - 3.067, медиана - 2.985.
Вывод по графикам для параметра reactive_power:
-- Среднее значение реактивной мощности необходимой для нагрева - 2.254, медиана - 2.177.
Вывод по графикам для параметра full_power:
-- Среднее значение активной мощности необходимой для нагрева - 3.811, медиана - 3.694.
Вывод по графикам для параметра heating_time:
-- Среднее значение времени необходимой для нагрева - 794.5 с, медиана - 770 с.
Анализ и предобработка данных датасета data_bulk_new¶
В датасете были столбцы с бошльшим количеством пропусков, удалим те в которых содержание пропусков более 85%
data_bulk_new = drop_high_na_columns(data_bulk_new)
# Проверка
data_bulk_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3129 entries, 0 to 3128 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3129 non-null int64 1 Bulk 3 1298 non-null float64 2 Bulk 4 1014 non-null float64 3 Bulk 6 576 non-null float64 4 Bulk 12 2450 non-null float64 5 Bulk 14 2806 non-null float64 6 Bulk 15 2248 non-null float64 dtypes: float64(6), int64(1) memory usage: 171.2 KB
Приведем название столбцов в датасете к формату PEP8
data_bulk_new = convert_to_pep8_column_names(data_bulk_new)
# Проверка
data_bulk_new.columns
Index(['key', 'bulk_3', 'bulk_4', 'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15'], dtype='object')
# Формирование списка столбцов с количественными признаками
num_variables_col = data_bulk_new.select_dtypes(include=['number']).columns.to_list()
num_variables_col.remove('key')
num_variables_col
['bulk_3', 'bulk_4', 'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15']
# Вывод графиков для количественных признаков датафрейма
for col in num_variables_col:
show_num_variable(data_bulk_new, col)
print('=' * TERM_SIZE.columns)
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
data_bulk_new.drop(columns=['key']).describe().round(3)
| bulk_3 | bulk_4 | bulk_6 | bulk_12 | bulk_14 | bulk_15 | |
|---|---|---|---|---|---|---|
| count | 1298.000 | 1014.000 | 576.000 | 2450.000 | 2806.000 | 2248.000 |
| mean | 113.879 | 104.394 | 118.925 | 260.471 | 170.285 | 160.513 |
| std | 75.483 | 48.184 | 72.058 | 120.649 | 65.869 | 51.765 |
| min | 6.000 | 12.000 | 17.000 | 53.000 | 16.000 | 1.000 |
| 25% | 58.000 | 72.000 | 69.750 | 204.000 | 119.000 | 105.000 |
| 50% | 97.500 | 102.000 | 100.000 | 208.000 | 151.000 | 160.000 |
| 75% | 152.000 | 133.000 | 157.000 | 316.000 | 205.750 | 205.000 |
| max | 454.000 | 281.000 | 503.000 | 1849.000 | 636.000 | 405.000 |
Вывод
Вывод по графикам для параметра bulk_3:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи сыпучего материала
bulk_3необходимой для нагрева - 113.9, медиана - 97.5.
Вывод по графикам для параметра bulk_4:
- Распределение не симметрично, а смещено вправо;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи сыпучего материала
bulk_4необходимой для нагрева - 104.4, медиана - 102.
Вывод по графикам для параметра bulk_6:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи сыпучего материала
bulk_6необходимой для нагрева - 118.9, медиана - 100.
Вывод по графикам для параметра bulk_12:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), также присутствует значение более 1750 похожее на аномалию (стоит уточнить у заказчика возможно ли теоретически такое значение и в случае подтверждения аномалии удалить данную запись);
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи сыпучего материала
bulk_12необходимой для нагрева - 260.5, медиана - 208.
Вывод по графикам для параметра bulk_14:
- Распределение не симметрично, а смещено вправо;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи сыпучего материала
bulk_14необходимой для нагрева - 170.3, медиана - 151.
Вывод по графикам для параметра bulk_15:
- Наблюдается двумодальное распределение;
- Среднее значение объема подачи сыпучего материала
bulk_15необходимой для нагрева - 160.5, медиана - 160, отсутствует сильная асимметрия – нет выраженного перекоса вправо или влево.
Анализ и предобработка данных датасета data_bulk_time_new¶
data_bulk_time_new.head(3)
| key | Bulk 1 | Bulk 2 | Bulk 3 | Bulk 4 | Bulk 5 | Bulk 6 | Bulk 7 | Bulk 8 | Bulk 9 | Bulk 10 | Bulk 11 | Bulk 12 | Bulk 13 | Bulk 14 | Bulk 15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | NaN | NaN | NaN | 2019-05-03 11:28:48 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 11:24:31 | NaN | 2019-05-03 11:14:50 | 2019-05-03 11:10:43 |
| 1 | 2 | NaN | NaN | NaN | 2019-05-03 11:36:50 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 11:53:30 | NaN | 2019-05-03 11:48:37 | 2019-05-03 11:44:39 |
| 2 | 3 | NaN | NaN | NaN | 2019-05-03 12:32:39 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2019-05-03 12:27:13 | NaN | 2019-05-03 12:21:01 | 2019-05-03 12:16:16 |
Приведем названия столбцов датасета к формату PEP8
data_bulk_time_new = convert_to_pep8_column_names(data_bulk_time_new)
# Проверка
data_bulk_time_new.columns
Index(['key', 'bulk_1', 'bulk_2', 'bulk_3', 'bulk_4', 'bulk_5', 'bulk_6',
'bulk_7', 'bulk_8', 'bulk_9', 'bulk_10', 'bulk_11', 'bulk_12',
'bulk_13', 'bulk_14', 'bulk_15'],
dtype='object')
Удалим столбцы содержащие большое количество пропусков (более 85%)
data_bulk_time_new = drop_high_na_columns(data_bulk_time_new)
# Проверка
data_bulk_time_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3129 entries, 0 to 3128 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3129 non-null int64 1 bulk_3 1298 non-null object 2 bulk_4 1014 non-null object 3 bulk_6 576 non-null object 4 bulk_12 2450 non-null object 5 bulk_14 2806 non-null object 6 bulk_15 2248 non-null object dtypes: int64(1), object(6) memory usage: 171.2+ KB
Переведем столбцы со временем в формат datetime
cols_to_convert = data_bulk_time_new.select_dtypes(include=['object'])
for col in cols_to_convert:
data_bulk_time_new[col] = pd.to_datetime(data_bulk_time_new[col], errors='coerce')
# Проверка
data_bulk_time_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3129 entries, 0 to 3128 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3129 non-null int64 1 bulk_3 1298 non-null datetime64[ns] 2 bulk_4 1014 non-null datetime64[ns] 3 bulk_6 576 non-null datetime64[ns] 4 bulk_12 2450 non-null datetime64[ns] 5 bulk_14 2806 non-null datetime64[ns] 6 bulk_15 2248 non-null datetime64[ns] dtypes: datetime64[ns](6), int64(1) memory usage: 171.2 KB
Вывод: для предсказания конечной температуры нагрева, время подачи сыпучих материалов не несет смысловой нагрузки, анализ данного датасета можно опуcтить.
Анализ и предобработка данных датасета data_gas_new¶
data_gas_new.head(1)
| key | Газ 1 | |
|---|---|---|
| 0 | 1 | 29.749986 |
Приведем название столбцов в датасете к формату PEP8
data_gas_new.columns = ['key', 'gas']
data_gas_new.columns
Index(['key', 'gas'], dtype='object')
show_num_variable(data_gas_new, 'gas')
data_gas_new['gas'].describe().round(3)
count 3239.000 mean 11.002 std 6.220 min 0.008 25% 7.043 50% 9.836 75% 13.770 max 77.995 Name: gas, dtype: float64
Вывод
Вывод по графикам для параметра gas:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи газа
gasнеобходимой для нагрева - 11, медиана - 9.836.
Анализ и предобработка данных датасета data_temp_new¶
data_temp_new.head(3)
| key | Время замера | Температура | |
|---|---|---|---|
| 0 | 1 | 2019-05-03 11:02:04 | 1571.0 |
| 1 | 1 | 2019-05-03 11:07:18 | 1604.0 |
| 2 | 1 | 2019-05-03 11:11:34 | 1618.0 |
Приведем название столбцов в датасете к формату PEP8
data_temp_new.columns = ['key', 'time', 'temperature']
data_temp_new.columns
Index(['key', 'time', 'temperature'], dtype='object')
Приведем столбец с временем к формату datetime
data_temp_new['time'] = pd.to_datetime(data_temp_new['time'], errors='coerce')
# Проверка
data_temp_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 18092 entries, 0 to 18091 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 18092 non-null int64 1 time 18092 non-null datetime64[ns] 2 temperature 14665 non-null float64 dtypes: datetime64[ns](1), float64(1), int64(1) memory usage: 424.2 KB
Проверим во всех ли партиях происходил замер температуры
summary = data_temp_new.groupby('key')['temperature'].agg(
total_count='count', # количество НЕ NaN
total_rows='size' # общее количество строк
)
# Ищем ключи, где ровно одно значение заполнено
keys_with_one_value = summary[summary['total_count'] == 1].index.tolist()
print("Ключи, где только одно значение temperature не пропущено:")
print(keys_with_one_value)
Ключи, где только одно значение temperature не пропущено: [195, 279, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, 2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553, 2554, 2555, 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, 2586, 2587, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, 2596, 2597, 2598, 2599, 2600, 2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645, 2646, 2647, 2648, 2649, 2650, 2651, 2652, 2653, 2654, 2655, 2656, 2657, 2658, 2659, 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, 2678, 2679, 2680, 2681, 2682, 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, 2739, 2740, 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, 2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, 2761, 2762, 2763, 2764, 2765, 2766, 2767, 2768, 2769, 2770, 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, 2780, 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, 2790, 2791, 2792, 2793, 2794, 2795, 2796, 2797, 2798, 2799, 2800, 2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811, 2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, 2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, 2841, 2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856, 2857, 2858, 2859, 2860, 2861, 2862, 2863, 2864, 2865, 2866, 2867, 2868, 2869, 2870, 2871, 2872, 2873, 2874, 2875, 2876, 2877, 2878, 2879, 2880, 2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, 2889, 2890, 2891, 2892, 2893, 2894, 2895, 2896, 2897, 2898, 2899, 2900, 2901, 2902, 2903, 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, 2921, 2922, 2923, 2924, 2925, 2926, 2927, 2928, 2929, 2930, 2931, 2932, 2933, 2934, 2935, 2936, 2937, 2938, 2939, 2940, 2941, 2942, 2943, 2944, 2945, 2946, 2947, 2948, 2949, 2950, 2951, 2952, 2953, 2954, 2955, 2956, 2957, 2958, 2959, 2960, 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2969, 2970, 2971, 2972, 2973, 2974, 2975, 2976, 2977, 2978, 2979, 2980, 2981, 2982, 2983, 2984, 2985, 2986, 2987, 2988, 2989, 2990, 2991, 2992, 2993, 2994, 2995, 2996, 2997, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, 3025, 3026, 3027, 3028, 3029, 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048, 3049, 3050, 3051, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062, 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, 3071, 3072, 3073, 3074, 3075, 3076, 3077, 3078, 3079, 3080, 3081, 3082, 3083, 3084, 3085, 3086, 3087, 3088, 3089, 3090, 3091, 3092, 3093, 3094, 3095, 3096, 3097, 3098, 3099, 3100, 3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109, 3110, 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, 3121, 3122, 3123, 3124, 3125, 3126, 3127, 3128, 3129, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138, 3139, 3140, 3141, 3142, 3143, 3144, 3145, 3146, 3147, 3148, 3149, 3150, 3151, 3152, 3153, 3154, 3155, 3156, 3157, 3158, 3159, 3160, 3161, 3162, 3163, 3164, 3165, 3166, 3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3175, 3176, 3177, 3178, 3179, 3180, 3181, 3182, 3183, 3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3195, 3196, 3197, 3198, 3199, 3201, 3202, 3203, 3204, 3205, 3206, 3208, 3209, 3210, 3211, 3212, 3213, 3214, 3215, 3216, 3217, 3218, 3219, 3220, 3221, 3222, 3223, 3224, 3225, 3226, 3227, 3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3239, 3240, 3241]
# Выберем подмножество ключей — например, первые 3 и последние 3
subset_keys = keys_with_one_value[:3] + keys_with_one_value[-3:]
# Проверим какие значения отфильтровались
filtered_data = data_temp_new[data_temp_new['key'].isin(subset_keys)]
print(filtered_data)
key time temperature 1105 195 2019-05-11 00:01:36 1583.0 1549 279 2019-05-14 10:13:01 1603.0 13926 2500 2019-08-10 14:04:39 1539.0 13927 2500 2019-08-10 14:13:11 NaN 13928 2500 2019-08-10 14:18:12 NaN 13929 2500 2019-08-10 14:25:53 NaN 13930 2500 2019-08-10 14:29:39 NaN 18071 3239 2019-09-06 14:16:50 1598.0 18072 3239 2019-09-06 14:22:49 NaN 18073 3239 2019-09-06 14:28:54 NaN 18074 3239 2019-09-06 14:33:34 NaN 18075 3239 2019-09-06 14:42:48 NaN 18076 3239 2019-09-06 14:56:58 NaN 18077 3239 2019-09-06 14:59:25 NaN 18078 3239 2019-09-06 15:03:35 NaN 18079 3239 2019-09-06 15:09:55 NaN 18080 3240 2019-09-06 15:25:21 1617.0 18081 3240 2019-09-06 15:30:52 NaN 18082 3240 2019-09-06 15:58:35 NaN 18083 3240 2019-09-06 16:02:31 NaN 18084 3240 2019-09-06 16:21:44 NaN 18085 3240 2019-09-06 16:35:26 NaN 18086 3241 2019-09-06 16:48:55 1586.0 18087 3241 2019-09-06 16:55:01 NaN 18088 3241 2019-09-06 17:06:38 NaN 18089 3241 2019-09-06 17:21:48 NaN 18090 3241 2019-09-06 17:24:44 NaN 18091 3241 2019-09-06 17:30:05 NaN
В партиях 195, 279 и с 2500 по 3241 замер температуры на этапах обработки не производился, такие партии для задачи не подходят - удалим их
data_temp_filtered = data_temp_new[~data_temp_new['key'].isin(keys_with_one_value)]
data_temp_filtered['key'].unique()
array([ 1, 2, 3, ..., 2497, 2498, 2499], dtype=int64)
Сгруппируем данные по партиям
data_temp_gp = data_temp_filtered.groupby(by = 'key').agg(['first', 'last'])
data_temp_gp.columns = ['first_time', 'finish_time', 'first_temp', 'finish_temp']
# Проверка
display(data_temp_gp.head())
print('Размерность получившегося датасета')
data_temp_gp.shape
| first_time | finish_time | first_temp | finish_temp | |
|---|---|---|---|---|
| key | ||||
| 1 | 2019-05-03 11:02:04 | 2019-05-03 11:30:38 | 1571.0 | 1613.0 |
| 2 | 2019-05-03 11:34:04 | 2019-05-03 11:55:09 | 1581.0 | 1602.0 |
| 3 | 2019-05-03 12:06:44 | 2019-05-03 12:35:57 | 1596.0 | 1599.0 |
| 4 | 2019-05-03 12:39:27 | 2019-05-03 12:59:47 | 1601.0 | 1625.0 |
| 5 | 2019-05-03 13:11:03 | 2019-05-03 13:36:39 | 1576.0 | 1602.0 |
Размерность получившегося датасета
(2475, 4)
Добавим параметр суммарного времени необходимого для замера нагрева в секундах
data_temp_gp['time_diff'] = (
data_temp_gp['finish_time'] - data_temp_gp['first_time']).dt.seconds
data_temp_gp.sample(5)
| first_time | finish_time | first_temp | finish_temp | time_diff | |
|---|---|---|---|---|---|
| key | |||||
| 610 | 2019-05-26 12:54:34 | 2019-05-26 13:23:27 | 1611.0 | 1602.0 | 1733 |
| 1544 | 2019-06-30 15:31:26 | 2019-06-30 16:29:28 | 1591.0 | 1590.0 | 3482 |
| 998 | 2019-06-10 23:08:28 | 2019-06-10 23:30:28 | 1586.0 | 1586.0 | 1320 |
| 1412 | 2019-06-25 15:02:20 | 2019-06-25 15:37:44 | 1546.0 | 1575.0 | 2124 |
| 992 | 2019-06-10 18:27:09 | 2019-06-10 18:58:14 | 1632.0 | 1593.0 | 1865 |
# Формирование списка столбцов с количественными признаками
num_variables_col = data_temp_gp.select_dtypes(include=['number']).columns.to_list()
num_variables_col
['first_temp', 'finish_temp', 'time_diff']
# Вывод графиков для количественных признаков датафрейма
for col in num_variables_col:
show_num_variable(data_temp_gp, col)
print('=' * TERM_SIZE.columns)
========================================================================================================================
========================================================================================================================
========================================================================================================================
show_num_variable(
data_temp_gp.assign(
temp_diff=data_temp_gp['finish_temp'] - data_temp_gp['first_temp']),
'temp_diff')
data_temp_gp.assign(
temp_diff=data_temp_gp['finish_temp'] - data_temp_gp['first_temp']
).describe().round(3)
| first_temp | finish_temp | time_diff | temp_diff | |
|---|---|---|---|---|
| count | 2475.000 | 2475.000 | 2475.000 | 2475.000 |
| mean | 1588.402 | 1595.336 | 2280.897 | 6.934 |
| std | 29.243 | 16.023 | 1373.706 | 27.549 |
| min | 1191.000 | 1541.000 | 339.000 | -98.000 |
| 25% | 1572.000 | 1587.000 | 1544.500 | -8.000 |
| 50% | 1588.000 | 1593.000 | 2009.000 | 7.000 |
| 75% | 1605.000 | 1599.000 | 2738.500 | 22.000 |
| max | 1679.000 | 1700.000 | 23674.000 | 408.000 |
Вывод
Вывод по графикам для параметра first_temp:
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется влево т.е, иногда встречаются маленькие значения (выбросы), но они редкие;
- Среднее значение начальной температуры
first_tempпри обработке материала - 1588, медиана - 1588.
Вывод по графикам для параметра finish_temp:
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение конечной температуры
finish_tempпри обработке материала - 1595, медиана - 1593.
Вывод по графикам для параметра time_diff:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение времени необходимого на замер температуры при обработке
time_diff- 2281, медиана - 2009.
Вывод по графикам для параметра temp_diff:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Среднее значение разности температуры между начальным и конечным этапом обработки
time_diff- 6.934, медиана - 7.
Анализ и предобработка данных датасета data_wire_new¶
data_wire_new.head(3)
| key | Wire 1 | Wire 2 | Wire 3 | Wire 4 | Wire 5 | Wire 6 | Wire 7 | Wire 8 | Wire 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 60.059998 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 2 | 96.052315 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | 3 | 91.160157 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Приведем наименования столбцов датасета к формату PEP8
data_wire_new = convert_to_pep8_column_names(data_wire_new)
# Проверка
data_wire_new.columns
Index(['key', 'wire_1', 'wire_2', 'wire_3', 'wire_4', 'wire_5', 'wire_6',
'wire_7', 'wire_8', 'wire_9'],
dtype='object')
Удалим столбцы содержащие большое количество пропусков (более 85%)
data_wire_new = drop_high_na_columns(data_wire_new)
# Проверка
data_wire_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3081 entries, 0 to 3080 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3081 non-null int64 1 wire_1 3055 non-null float64 2 wire_2 1079 non-null float64 dtypes: float64(2), int64(1) memory usage: 72.3 KB
# Формирование списка столбцов с количественными признаками
num_variables_col = data_wire_new.select_dtypes(include=['number']).columns.to_list()
num_variables_col.remove('key')
num_variables_col
['wire_1', 'wire_2']
# Вывод графиков для количественных признаков датафрейма
for col in num_variables_col:
show_num_variable(data_wire_new, col)
print('=' * TERM_SIZE.columns)
========================================================================================================================
========================================================================================================================
data_wire_new.drop(columns=['key']).describe().round(3)
| wire_1 | wire_2 | |
|---|---|---|
| count | 3055.000 | 1079.000 |
| mean | 100.896 | 50.577 |
| std | 42.013 | 39.320 |
| min | 1.919 | 0.030 |
| 25% | 72.116 | 20.194 |
| 50% | 100.158 | 40.143 |
| 75% | 126.060 | 70.228 |
| max | 330.314 | 282.780 |
Вывод
Вывод по графикам для параметра wire_1:
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- Среднее значение объема подачи проволочного материала
wire_1при обработке - 100.9, медиана - 100.2.
Вывод по графикам для параметра wire_2:
- Распределение не симметрично, а смещено вправо;
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- На графиках изображена правосторонняя асимметрия т.к. медиана < среднего.
- Среднее значение объема подачи проволочного материала
wire_2при обработке - 50.58, медиана - 40.14.
Анализ и предобработка данных датасета data_wire_time_new¶
data_wire_time_new.head(3)
| key | Wire 1 | Wire 2 | Wire 3 | Wire 4 | Wire 5 | Wire 6 | Wire 7 | Wire 8 | Wire 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2019-05-03 11:06:19 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 2 | 2019-05-03 11:36:50 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | 3 | 2019-05-03 12:11:46 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Приведем названия столбцов датасета к формату PEP8
data_wire_time_new = convert_to_pep8_column_names(data_wire_time_new)
# Проверка
data_wire_time_new.columns
Index(['key', 'wire_1', 'wire_2', 'wire_3', 'wire_4', 'wire_5', 'wire_6',
'wire_7', 'wire_8', 'wire_9'],
dtype='object')
Удалим столбцы содержащие большое количество пропусков (более 85%)
data_wire_time_new = drop_high_na_columns(data_wire_time_new)
# Проверка
data_wire_time_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3081 entries, 0 to 3080 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3081 non-null int64 1 wire_1 3055 non-null object 2 wire_2 1079 non-null object dtypes: int64(1), object(2) memory usage: 72.3+ KB
Переведем столбцы со временем в формат datetime
cols_to_convert = data_wire_time_new.select_dtypes(include=['object'])
for col in cols_to_convert:
data_wire_time_new[col] = pd.to_datetime(data_wire_time_new[col], errors='coerce')
# Проверка
data_wire_time_new.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3081 entries, 0 to 3080 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 3081 non-null int64 1 wire_1 3055 non-null datetime64[ns] 2 wire_2 1079 non-null datetime64[ns] dtypes: datetime64[ns](2), int64(1) memory usage: 72.3 KB
Вывод: для предсказания конечной температуры нагрева, время подачи проволочных материалов не несет смысловой нагрузки, анализ данного датасета можно опуcтить.
Вывод
- Привели названия столбцов к формату PEP8;
- Обработали пропуски, аномалии и неверный тип данных: удалили столбцы где значения превышают 85%, убрали аномалии по отрицательной мощности и привели все временные данные к формату datetime;
- Добавили дополнительные признаки там, где это показалось уместным.
- Подготовили данные для объединения в общий датасет. Также удалили данные с одним измерением температуры.
Объединение данных¶
Для объединения датасетов используем все, кроме data_bulk_time_new и data_wire_time_new
data = data_arc_new_gp.merge(data_temp_gp, on='key', how='inner')
data = data.merge(data_gas_new, on='key', how='inner')
data = data.merge(data_bulk_new, on='key', how='inner')
data = data.merge(data_wire_new, on='key', how='inner')
show_info(data)
| key | active_power | reactive_power | full_power | heating_time | first_time | finish_time | first_temp | finish_temp | time_diff | gas | bulk_3 | bulk_4 | bulk_6 | bulk_12 | bulk_14 | bulk_15 | wire_1 | wire_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 3.036730 | 2.142821 | 3.718736 | 1098 | 2019-05-03 11:02:04 | 2019-05-03 11:30:38 | 1571.0 | 1613.0 | 1714 | 29.749986 | NaN | 43.0 | NaN | 206.0 | 150.0 | 154.0 | 60.059998 | NaN |
| 1 | 2 | 2.139408 | 1.453357 | 2.588349 | 811 | 2019-05-03 11:34:04 | 2019-05-03 11:55:09 | 1581.0 | 1602.0 | 1265 | 12.555561 | NaN | 73.0 | NaN | 206.0 | 149.0 | 154.0 | 96.052315 | NaN |
| 2 | 3 | 4.063641 | 2.937457 | 5.019223 | 655 | 2019-05-03 12:06:44 | 2019-05-03 12:35:57 | 1596.0 | 1599.0 | 1753 | 28.554793 | NaN | 34.0 | NaN | 205.0 | 152.0 | 153.0 | 91.160157 | NaN |
| 3 | 4 | 2.706489 | 2.056992 | 3.400038 | 741 | 2019-05-03 12:39:27 | 2019-05-03 12:59:47 | 1601.0 | 1625.0 | 1220 | 18.841219 | NaN | 81.0 | NaN | 207.0 | 153.0 | 154.0 | 89.063515 | NaN |
| 4 | 5 | 2.252950 | 1.687991 | 2.816980 | 869 | 2019-05-03 13:11:03 | 2019-05-03 13:36:39 | 1576.0 | 1602.0 | 1536 | 5.413692 | NaN | 78.0 | NaN | 203.0 | 151.0 | 152.0 | 89.238236 | 9.11456 |
<class 'pandas.core.frame.DataFrame'> Int64Index: 2329 entries, 0 to 2328 Data columns (total 19 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 key 2329 non-null int64 1 active_power 2329 non-null float64 2 reactive_power 2329 non-null float64 3 full_power 2329 non-null float64 4 heating_time 2329 non-null int64 5 first_time 2329 non-null datetime64[ns] 6 finish_time 2329 non-null datetime64[ns] 7 first_temp 2329 non-null float64 8 finish_temp 2329 non-null float64 9 time_diff 2329 non-null int64 10 gas 2329 non-null float64 11 bulk_3 960 non-null float64 12 bulk_4 812 non-null float64 13 bulk_6 438 non-null float64 14 bulk_12 1812 non-null float64 15 bulk_14 2068 non-null float64 16 bulk_15 1699 non-null float64 17 wire_1 2306 non-null float64 18 wire_2 811 non-null float64 dtypes: datetime64[ns](2), float64(14), int64(3) memory usage: 363.9 KB ======================================================================================================================== Количество дубликатов: 0 ======================================================================================================================== Процент пропусков от всего датасета: bulk_6 81.2% wire_2 65.2% bulk_4 65.1% bulk_3 58.8% bulk_15 27.1% bulk_12 22.2% bulk_14 11.2% wire_1 1.0% gas 0.0% key 0.0% active_power 0.0% finish_temp 0.0% first_temp 0.0% finish_time 0.0% first_time 0.0% heating_time 0.0% full_power 0.0% reactive_power 0.0% time_diff 0.0% dtype: object ======================================================================================================================== Описание:
| key | active_power | reactive_power | full_power | heating_time | first_temp | finish_temp | time_diff | gas | bulk_3 | bulk_4 | bulk_6 | bulk_12 | bulk_14 | bulk_15 | wire_1 | wire_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 2329.000000 | 2329.000000 | 2329.000000 | 2329.000000 | 2329.000000 | 2329.000000 | 2329.000000 | 2329.000000 | 2329.000000 | 960.000000 | 812.000000 | 438.000000 | 1812.000000 | 2068.000000 | 1699.000000 | 2306.000000 | 811.000000 |
| mean | 1251.832546 | 3.125033 | 2.300522 | 3.884654 | 807.600687 | 1586.718763 | 1593.365393 | 2322.899957 | 11.375600 | 114.868750 | 106.995074 | 119.269406 | 267.880795 | 173.271277 | 164.432019 | 103.465371 | 50.571346 |
| std | 714.762400 | 1.221007 | 0.903968 | 1.518157 | 340.897332 | 28.290792 | 11.200915 | 1384.892092 | 6.392041 | 77.485694 | 49.050943 | 70.747953 | 125.588642 | 64.009860 | 50.039060 | 42.530971 | 39.755956 |
| min | 1.000000 | 0.267676 | 0.196228 | 0.331897 | 57.000000 | 1191.000000 | 1541.000000 | 339.000000 | 0.008399 | 6.000000 | 13.000000 | 17.000000 | 53.000000 | 29.000000 | 1.000000 | 1.918800 | 0.030160 |
| 25% | 630.000000 | 2.293900 | 1.669572 | 2.843058 | 581.000000 | 1571.000000 | 1587.000000 | 1581.000000 | 7.282948 | 57.000000 | 73.000000 | 72.000000 | 204.000000 | 123.000000 | 105.000000 | 75.042236 | 20.193680 |
| 50% | 1255.000000 | 3.035365 | 2.225398 | 3.767499 | 778.000000 | 1587.000000 | 1593.000000 | 2047.000000 | 10.100950 | 96.500000 | 105.000000 | 100.000000 | 208.000000 | 153.000000 | 200.000000 | 102.053638 | 40.112801 |
| 75% | 1868.000000 | 3.834300 | 2.829159 | 4.769421 | 993.000000 | 1603.000000 | 1598.000000 | 2791.000000 | 14.216688 | 152.250000 | 136.250000 | 155.750000 | 359.250000 | 208.000000 | 205.000000 | 128.220310 | 69.699761 |
| max | 2499.000000 | 12.375636 | 8.949049 | 15.288271 | 4189.000000 | 1660.000000 | 1653.000000 | 23674.000000 | 77.995040 | 454.000000 | 281.000000 | 503.000000 | 1849.000000 | 636.000000 | 405.000000 | 330.314424 | 282.780152 |
========================================================================================================================
Размер: (2329, 19)
Вывод
Мы объединили все необходимые датасеты в один итоговый.
Исследовательский анализ и предобработка данных объединённого датафрейма
В объединенном датасете присутствуют пропуски в подаче материалов wire и bulk, заменим их на значение 0
data = data.fillna(0)
data.isna().sum()
key 0 active_power 0 reactive_power 0 full_power 0 heating_time 0 first_time 0 finish_time 0 first_temp 0 finish_temp 0 time_diff 0 gas 0 bulk_3 0 bulk_4 0 bulk_6 0 bulk_12 0 bulk_14 0 bulk_15 0 wire_1 0 wire_2 0 dtype: int64
В результате заполнения пропуски в датасете исчезли
num_variables_col = data.select_dtypes(include=['number'])
data_corr = data.phik_matrix(interval_cols=num_variables_col)
plt.figure(figsize=(14, 10))
sns.heatmap(data_corr, annot=True, fmt=".2f", center=0)
plt.title('Матрица корреляций phik для объединенного датафрейма')
plt.show()
Удалим столбцы со временем first_time и finish_time вызывающую мультиколлинеарность, а также столбец c номером key не несущий смысловой нагрузки
data = data.drop(columns=['key', 'first_time', 'finish_time'])
num_variables_col = data.select_dtypes(include=['number'])
data_corr = data.phik_matrix(interval_cols=num_variables_col)
plt.figure(figsize=(14, 10))
sns.heatmap(data_corr, annot=True, fmt=".2f", center=0)
plt.title('Матрица корреляций phik для объединенного датафрейма')
plt.show()
Наблюдается высокая корреляция признаков наблюдается между: active_power, reactive_power, full_power, т.к. данные значения связаны физической формулой.
Для целевого признака finish_temp наибольшая корреляция заметна с параметрами active_power, full_power и wire_1.
# Вывод графиков для количественных признаков датафрейма
for col in num_variables_col:
show_num_variable(data, col)
print('=' * TERM_SIZE.columns)
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
========================================================================================================================
data.describe().round(3)
| active_power | reactive_power | full_power | heating_time | first_temp | finish_temp | time_diff | gas | bulk_3 | bulk_4 | bulk_6 | bulk_12 | bulk_14 | bulk_15 | wire_1 | wire_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 | 2329.000 |
| mean | 3.125 | 2.301 | 3.885 | 807.601 | 1586.719 | 1593.365 | 2322.900 | 11.376 | 47.348 | 37.304 | 22.430 | 208.416 | 153.854 | 119.953 | 102.444 | 17.610 |
| std | 1.221 | 0.904 | 1.518 | 340.897 | 28.291 | 11.201 | 1384.892 | 6.392 | 75.310 | 58.643 | 55.791 | 157.062 | 81.404 | 84.640 | 43.540 | 33.625 |
| min | 0.268 | 0.196 | 0.332 | 57.000 | 1191.000 | 1541.000 | 339.000 | 0.008 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 |
| 25% | 2.294 | 1.670 | 2.843 | 581.000 | 1571.000 | 1587.000 | 1581.000 | 7.283 | 0.000 | 0.000 | 0.000 | 105.000 | 105.000 | 0.000 | 73.208 | 0.000 |
| 50% | 3.035 | 2.225 | 3.767 | 778.000 | 1587.000 | 1593.000 | 2047.000 | 10.101 | 0.000 | 0.000 | 0.000 | 206.000 | 149.000 | 107.000 | 101.119 | 0.000 |
| 75% | 3.834 | 2.829 | 4.769 | 993.000 | 1603.000 | 1598.000 | 2791.000 | 14.217 | 80.000 | 77.000 | 0.000 | 282.000 | 204.000 | 204.000 | 128.092 | 23.103 |
| max | 12.376 | 8.949 | 15.288 | 4189.000 | 1660.000 | 1653.000 | 23674.000 | 77.995 | 454.000 | 281.000 | 503.000 | 1849.000 | 636.000 | 405.000 | 330.314 | 282.780 |
Вывод по графикам
Вывод по графикам для параметров active_power, reactive_power, full_power, heating_time gas, time_diff и bulk_12:
- На графике имеется один пик — большинство значений сосредоточено в узком диапазоне;
- Длинный "хвост" тянется вправо т.е, иногда встречаются более крупные значения (выбросы), но они редкие;
- Распределение не симметрично, а смещено вправо.
Вывод по графикам для параметра first_temp:
- Распределение не симметрично, а смещено влево.
- Значение медианы и среднего различаются менее чем на 1%.
Вывод по графикам для параметра finish_temp и wire_1:
- График имеет нормальное распределение;
- Значение медианы и среднего различаются менее чем на 1%.
Вывод по графикам для параметров bulk_3, bulk_4, bulk_6 и wire_2:
- Графики имеют одномодальное распределение со значением близким к 0 и длинными "хвостами" тянущиеся вправо.
Вывод по графикам для параметра bulk_14 и bulk_15:
- Графики имеют трехмодальное распределение с 3-мя выраженными пиками.
Подготовка данных¶
X = data.drop('finish_temp', axis=1)
y = data['finish_temp']
num_columns = X.select_dtypes(include=['number']).columns.tolist()
# Вывод списка столбцов с количественными данными
display(num_columns)
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=TEST_SIZE,
random_state=RANDOM_STATE
)
['active_power', 'reactive_power', 'full_power', 'heating_time', 'first_temp', 'time_diff', 'gas', 'bulk_3', 'bulk_4', 'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15', 'wire_1', 'wire_2']
print('Размер X_train: ', X_train.shape)
print('Размер X_test: ', X_test.shape)
print('Размер y_train: ', y_train.shape)
print('Размер y_test: ', y_test.shape)
Размер X_train: (1746, 15) Размер X_test: (583, 15) Размер y_train: (1746,) Размер y_test: (583,)
Вывод
Данные были подготовлены для обучения моделей и разделены в соотношении 3:1 в соответствии с заданием
Обучение моделей машинного обучения¶
# Обработка числовых и категориальных признаков с импутацией
num_transformer = Pipeline(steps=[
("imputer", SimpleImputer(strategy='constant', fill_value=0)), # Заполнение пропусков 0
("scaler", StandardScaler())
])
preprocessor = ColumnTransformer([
('num', num_transformer, num_columns),
])
pipe = Pipeline([
('preprocessor', preprocessor),
('model', LinearRegression())
])
param_grid = [
{
'preprocessor__num__scaler': [StandardScaler(), MinMaxScaler(), 'passthrough'],
'model': [DummyRegressor()],
'model__strategy': ['mean', 'median']
},
{
'preprocessor__num__scaler': [StandardScaler(), MinMaxScaler(), 'passthrough'],
'model': [LinearRegression()],
},
{
'preprocessor__num__scaler': ['passthrough'], # Для деревьев решений scaler не требуется
'model': [RandomForestRegressor(
random_state=RANDOM_STATE,
n_jobs=-1
)],
'model__n_estimators': [50, 100, 200],
'model__max_depth': [5, 10],
'model__min_samples_split': [2, 5],
'model__min_samples_leaf': [1, 2, 4],
'model__criterion': ['absolute_error']
},
{
'preprocessor__num__scaler': ['passthrough'], # Для деревьев решений scaler не требуется
'model': [
CatBoostRegressor(
random_state=RANDOM_STATE,
verbose=False,
loss_function='MAE',
iterations=1000,
thread_count=multiprocessing.cpu_count(),
early_stopping_rounds=50
)
],
'model__learning_rate': [0.01, 0.1]
}
]
grid_search = GridSearchCV(
pipe,
param_grid,
n_jobs=-1,
cv=5,
scoring='neg_mean_absolute_error'
)
grid_search.fit(X_train, y_train)
GridSearchCV(cv=5,
estimator=Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler',
StandardScaler())]),
['active_power',
'reactive_power',
'full_power',
'heating_time',
'first_temp',
'time_diff',
'gas',
'bulk_3',
'bulk_4',
'bulk_6',
'bulk_12',
'bulk_14',
'bulk_...
'model__max_depth': [5, 10],
'model__min_samples_leaf': [1, 2, 4],
'model__min_samples_split': [2, 5],
'model__n_estimators': [50, 100, 200],
'preprocessor__num__scaler': ['passthrough']},
{'model': [<catboost.core.CatBoostRegressor object at 0x00000257E6A95640>],
'model__learning_rate': [0.01, 0.1],
'preprocessor__num__scaler': ['passthrough']}],
scoring='neg_mean_absolute_error')In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
GridSearchCV(cv=5,
estimator=Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler',
StandardScaler())]),
['active_power',
'reactive_power',
'full_power',
'heating_time',
'first_temp',
'time_diff',
'gas',
'bulk_3',
'bulk_4',
'bulk_6',
'bulk_12',
'bulk_14',
'bulk_...
'model__max_depth': [5, 10],
'model__min_samples_leaf': [1, 2, 4],
'model__min_samples_split': [2, 5],
'model__n_estimators': [50, 100, 200],
'preprocessor__num__scaler': ['passthrough']},
{'model': [<catboost.core.CatBoostRegressor object at 0x00000257E6A95640>],
'model__learning_rate': [0.01, 0.1],
'preprocessor__num__scaler': ['passthrough']}],
scoring='neg_mean_absolute_error')Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler',
'passthrough')]),
['active_power',
'reactive_power',
'full_power', 'heating_time',
'first_temp', 'time_diff',
'gas', 'bulk_3', 'bulk_4',
'bulk_6', 'bulk_12',
'bulk_14', 'bulk_15',
'wire_1', 'wire_2'])])),
('model',
<catboost.core.CatBoostRegressor object at 0x00000257E6C43610>)])ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler', 'passthrough')]),
['active_power', 'reactive_power',
'full_power', 'heating_time', 'first_temp',
'time_diff', 'gas', 'bulk_3', 'bulk_4',
'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15',
'wire_1', 'wire_2'])])['active_power', 'reactive_power', 'full_power', 'heating_time', 'first_temp', 'time_diff', 'gas', 'bulk_3', 'bulk_4', 'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15', 'wire_1', 'wire_2']
SimpleImputer(fill_value=0, strategy='constant')
passthrough
<catboost.core.CatBoostRegressor object at 0x00000257E6C43610>
Вывод
На тренировочной выборке были обучены 4 модели: DummyRegressor(), LinearRegression(), RandomForestRegressor() и CatBoostRegressor().
Выбор лучшей модели¶
# Вывод результатов
print(f"Лучший результат MAE: {-grid_search.best_score_:.2f}")
print("Лучшие параметры:", grid_search.best_params_)
Лучший результат MAE: 5.97
Лучшие параметры: {'model': <catboost.core.CatBoostRegressor object at 0x00000257E6A95640>, 'model__learning_rate': 0.01, 'preprocessor__num__scaler': 'passthrough'}
Лучший результат MAE = 5.97 показала модель CatBoostRegressor() с гиперпараметрами 'model__learning_rate': 0.01, 'preprocessor__num__scaler': 'passthrough'.
Используем ее для предсказания на тестовой выборке
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
print(f"✅ MAE на тестовых данных: {mae:.2f}")
✅ MAE на тестовых данных: 6.50
Метрика МАЕ=6.5 на тестовой выборке, что удовлетворяет условию задачи (менее 6.8)
Проверка на адекватность¶
Проверим данную модель на адекватность сравнив её с моделью DummyRegressor на тестовой выборке, модель делает предсказание как среднее или медианное значение
dummy_grid = [
{
'preprocessor__num__scaler': [StandardScaler(), MinMaxScaler(), 'passthrough'],
'model': [DummyRegressor()],
'model__strategy': ['mean', 'median']
}
]
grid_search_dummy = GridSearchCV(
pipe,
dummy_grid,
n_jobs=-1,
cv=5,
scoring='neg_mean_absolute_error'
)
grid_search_dummy.fit(X_train, y_train)
GridSearchCV(cv=5,
estimator=Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler',
StandardScaler())]),
['active_power',
'reactive_power',
'full_power',
'heating_time',
'first_temp',
'time_diff',
'gas',
'bulk_3',
'bulk_4',
'bulk_6',
'bulk_12',
'bulk_14',
'bulk_15',
'wire_1',
'wire_2'])])),
('model', LinearRegression())]),
n_jobs=-1,
param_grid=[{'model': [DummyRegressor()],
'model__strategy': ['mean', 'median'],
'preprocessor__num__scaler': [StandardScaler(),
MinMaxScaler(),
'passthrough']}],
scoring='neg_mean_absolute_error')In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
GridSearchCV(cv=5,
estimator=Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler',
StandardScaler())]),
['active_power',
'reactive_power',
'full_power',
'heating_time',
'first_temp',
'time_diff',
'gas',
'bulk_3',
'bulk_4',
'bulk_6',
'bulk_12',
'bulk_14',
'bulk_15',
'wire_1',
'wire_2'])])),
('model', LinearRegression())]),
n_jobs=-1,
param_grid=[{'model': [DummyRegressor()],
'model__strategy': ['mean', 'median'],
'preprocessor__num__scaler': [StandardScaler(),
MinMaxScaler(),
'passthrough']}],
scoring='neg_mean_absolute_error')Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler',
StandardScaler())]),
['active_power',
'reactive_power',
'full_power', 'heating_time',
'first_temp', 'time_diff',
'gas', 'bulk_3', 'bulk_4',
'bulk_6', 'bulk_12',
'bulk_14', 'bulk_15',
'wire_1', 'wire_2'])])),
('model', DummyRegressor(strategy='median'))])ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value=0,
strategy='constant')),
('scaler', StandardScaler())]),
['active_power', 'reactive_power',
'full_power', 'heating_time', 'first_temp',
'time_diff', 'gas', 'bulk_3', 'bulk_4',
'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15',
'wire_1', 'wire_2'])])['active_power', 'reactive_power', 'full_power', 'heating_time', 'first_temp', 'time_diff', 'gas', 'bulk_3', 'bulk_4', 'bulk_6', 'bulk_12', 'bulk_14', 'bulk_15', 'wire_1', 'wire_2']
SimpleImputer(fill_value=0, strategy='constant')
StandardScaler()
DummyRegressor(strategy='median')
# Вывод результатов
print(f"Лучший результат MAE: {-grid_search_dummy.best_score_:.2f}")
print("Лучшие параметры:", grid_search_dummy.best_params_)
Лучший результат MAE: 7.92
Лучшие параметры: {'model': DummyRegressor(), 'model__strategy': 'median', 'preprocessor__num__scaler': StandardScaler()}
На обучающей выборке модель DummyRegressor() с медианными значениями и масштабированием с помощью StandardScaler() показала метрику МАЕ=7.92
dummy_model = grid_search_dummy.best_estimator_
y_pred_dummy = dummy_model.predict(X_test)
mae_dummy = mean_absolute_error(y_test, y_pred_dummy)
print(f"✅ MAE на тестовых данных: {mae_dummy:.2f}")
✅ MAE на тестовых данных: 8.54
Модель предсказывающая медиану финвльной температуры показала метрику MAE=8.54 на тестовой выборке, что больше чем модель CatBoostRegressor() с MAE=6.5.
Это подтверждает адекватность и целесообразность использования для предсказания финальной температуры после обработки материала CatBoostRegressor().
Анализ важности признаков для лучшей модели¶
catboost_model = best_model.named_steps['model']
feature_names = best_model.named_steps['preprocessor'].transformers_[0][2]
feature_importance_data = pd.DataFrame({
'feature': feature_names,
'importance': catboost_model.feature_importances_
})
feature_importance_data = feature_importance_data.sort_values(by='importance', ascending=False)
plt.figure(figsize=(12, 8))
sns.barplot(x='importance', y='feature', data=feature_importance_data)
plt.title('Важность признаков для CatBoostRegressor', fontsize=16)
plt.xlabel('Важность признака')
plt.ylabel('Входной признак')
plt.tight_layout()
plt.show()
Для модели наиболее важными признаками являются:
- время требующееся на нагрев;
- начальная температура материала.
Вывол
- Была выбрана лучшая модель - CatBoostRegressor с метрикой
MAE=6.5на тестовой выборке; - Была произведена проверка лучшей модели на адекватность путем ее сравнения с моделью DummyRegressor на тестовой выборке;
- Были определены наиболее важные входные признаки для предсказания моделью целевого признака
finish_temp(конечная температура после обработки).
Общий вывод¶
- Были загружены и проанализированы полученные датасеты.
- В ходе предобработки и исследовательского анализа датасетов:
- названия столбцов датасетов было приведено к формату PEP8;
- столбцы с временными данными были приведены к формату datetime;
- столбцы с большим количеством пропусков (более 85%) были убраны из датасетов;
- необходимые датасеты были сгруппированы по партиям;
- были убраны партии с аномальными значениями реактивной мощности < 0;
- были созданы новые входные признаки из данных находящихся в датасете;
- был проведен статистический анализ датасетов и сделаны выводы.
- Была создана объединенная таблица, в которой использовали все датасеты, кроме
data_bulk_time_newиdata_wire_time_new. Итоговый размер датасета (2329, 19). - Была произведена предобработка датасета и его анализ в ходе которых:
- все пропуски в столбцах подачи материалов заменены на 0;
- были удалены столбцы
key,first_timeиfinish_timeне несущие смысловой нагрузки или создающие мультиколлинеарность в датасете; - был проведен корреляционный и статистический анализ итогового датасета для машинного обучения.
- Были подготовлены данные для МО, датасет был разделен на тренировочную и тестовую выборку в соотношении 3:1.
- Были обучены 4 модели машинного обучения:
DummyRegressor,LinearRegression,RandomForestRegressorиCatBoostRegressor. И подобраны гиперпараметры с помощьюGridSearchCV. - Был произведен выбор лучшей модели и ее анализ:
- в ходе подбора гиперпараметров с помощью
GridSearchCVбыла определена лучшая модель на обучающей выборке - ей оказаласьCatBoostRegressorс гиперпараметрами ('model__learning_rate': 0.01, 'preprocessor__num__scaler': 'passthrough') и метрикой качестваMAE=5.97; - модель
CatBoostRegressorпоказала удволетворяющее условию качество на тестовой выборкеMAE=6.5(менее 6.8); - модель
CatBoostRegressorбыла проверена на адекватность путем сравнения ее метрики качества МАЕ на тестовой выборке с констанстной модельюDummyRegressor. Метрика качества МАЕ для моделиCatBoostRegressorоказалась ниже (6.5 против 8.54). Что подтвердило адекватность и целесообразность использования для предсказания финальной температуры после обработки материала полученную модель CatBoostRegressor(); - был проведен анализ важности входных признаков на предсказание модели, наиболее важными признаками являются:
heating_time(время требующееся на нагрев) иfirst_tempначальная температура материала.
- в ходе подбора гиперпараметров с помощью
Рекомендация в ходе проделанной работы - уделить внимание точности и своевременности замеров температуры.